import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from Perceptron import plot_decision_regions

class AdalineGD(object):

    def __init__(self, eta=0.01, n_iter=100):
        self.eta = eta
        self.n_iter = n_iter

    def fit(self, X, y):

        self.w_ = np.zeros(1 + X.shape[1])
        self.cost_ = [] #记录每轮迭代的损失值

        for i in range(self.n_iter):
            output = self.net_input(X)
            errors = (y - output) #  计算误差向量（实际值 - 预测值）
            # 权重更新（批量梯度下降）
            self.w_[1:] += self.eta * X.T.dot(errors) # 更新特征权重：η * Σ(x_j * error)
            self.w_[0] += self.eta * errors.sum() # 更新偏置项：η * Σ(error)
            cost = (errors**2).sum() / 2.0 # 平方误差和除以2（SSE/2）
            self.cost_.append(cost)   # 记录当前迭代的损失值
        return self # 返回训练后的模型实例（支持链式调用）

    def net_input(self, X):
        """Calculate net input"""
        #  计算 $z = w^Tx + b$（线性组合）
        return np.dot(X, self.w_[1:]) + self.w_[0]

    def activation(self, X):
        """Compute linear activation"""
        return self.net_input(X)

    def predict(self, X):
        """Return class label after unit step"""
        return np.where(self.activation(X) >= 0.0, 1, -1)




df = pd.read_csv('https://archive.ics.uci.edu/ml/'
                 'machine-learning-databases/iris/iris.data', header=None)
# 这个CSV文件有5列，前4列是特征，最后一列是鸢尾花的品种（字符串类型）。由于没有表头，pandas会默认将列命名为0,1,2,3,4。
print(50 * '=')
print(df)
print("----------------------------")
# 查看数据集的最后5行
print(df.tail())

#############################################################################
print(50 * '=')
print('Plotting the Iris data')
print(50 * '-')

# select setosa and versicolor
# 选择前100个样本（Iris-setosa和Iris-versicolor）
""" 
# 提取特征矩阵 X（前100行的前4列）
X = df.iloc[0:100, [0, 1, 2, 3]].values

# 提取标签向量 y
y = df.iloc[0:100, 4].values

"""
y = df.iloc[0:100, 4].values
# # 创建二进制标签 (setosa: -1, versicolor: 1)
y = np.where(y == 'Iris-setosa', -1, 1)

# extract sepal length and petal length
"""
行索引 0:100：选取前100个样本（排除第三类 virginica）
列索引 [0, 2]：选择萼片长度（第0列）和花瓣长度（第2列"""
X = df.iloc[0:100, [0, 2]].values




fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))

ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')

ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')

# plt.tight_layout()
# plt.savefig('./adaline_1.png', dpi=300)
plt.show()







print('standardize features')
X_std = np.copy(X)
X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()
X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std()

ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)

plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
# plt.tight_layout()
# plt.savefig('./adaline_2.png', dpi=300)
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')

# plt.tight_layout()
# plt.savefig('./adaline_3.png', dpi=300)
plt.show()


#############################################################################
print(50 * '=')
print('Large scale machine learning and stochastic gradient descent')
print(50 * '-')
